package com.github.maojx0630.translate.translate.dict.select;

import cn.hutool.core.collection.CollUtil;
import com.github.maojx0630.translate.TranslateConfig;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.TextSqlNode;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @author 毛家兴
 */
public class DefaultTranslateSelectImpl implements TranslateSelect {

    private static final Logger log = LoggerFactory.getLogger(DefaultTranslateSelectImpl.class);
    private final String dictMsId = "selectWithDict";
    private final String tableMsId = "selectWithTable";
    private final Map<String, MappedStatement> cacheMap = new HashMap<>();
    private final SqlSessionFactory factory;
    private final TranslateConfig translateConfig;
    private final TranslateConfig.TableConfig tableConfig;
    private final TranslateConfig.DictConfig dictConfig;

    public DefaultTranslateSelectImpl(SqlSessionFactory factory, TranslateConfig config) {
        this.factory = factory;
        this.translateConfig = config;
        this.tableConfig = config.getTableConfig();
        this.dictConfig = config.getDictConfig();
        try (SqlSession sqlSession = factory.openSession()) {
            Configuration configuration = sqlSession.getConfiguration();
            String dictMsSql =
                    "select ${label} from ${table} where ${type}=#{typeValue} and ${value}=#{inValue} ${lastSql}";
            MappedStatement dictMs =
                    buildMappedStatement(sqlSession.getConfiguration(), dictMsId, dictMsSql);
            configuration.addMappedStatement(dictMs);
            String tableMsSql =
                    "select ${columnName} from ${tableName} where ${valueColumnName} = #{inValue} ${lastSql}";
            MappedStatement tableMs =
                    buildMappedStatement(sqlSession.getConfiguration(), tableMsId, tableMsSql);
            configuration.addMappedStatement(tableMs);
            cacheMap.put(dictMsId, dictMs);
            cacheMap.put(tableMsId, tableMs);
        }
    }

    private static MappedStatement buildMappedStatement(
            Configuration configuration, String id, String sql) {
        DynamicSqlSource sqlSource = new DynamicSqlSource(configuration, new TextSqlNode(sql));
        MappedStatement.Builder statementBuilder =
                new MappedStatement.Builder(configuration, id, sqlSource, SqlCommandType.SELECT);
        statementBuilder.parameterMap(createParameterMap(configuration));
        statementBuilder.resultMaps(createResultMaps(configuration));
        return statementBuilder.build();
    }

    // 创建参数映射
    private static ParameterMap createParameterMap(Configuration configuration) {
        configuration.getTypeHandlerRegistry().getTypeHandler((Class<?>) Map.class);
        return new ParameterMap.Builder(configuration, "default", Map.class, new ArrayList<>()).build();
    }

    // 创建结果映射
    private static List<ResultMap> createResultMaps(Configuration configuration) {
        ResultMap.Builder resultMapBuilder =
                new ResultMap.Builder(configuration, "default", String.class, new ArrayList<>());
        return Collections.singletonList(resultMapBuilder.build());
    }

    @Override
    public String selectDict(String typeValue, String inValue, String lastSql, String remark) {
        Map<String, String> map = new HashMap<>();
        map.put("label", dictConfig.getLabel());
        map.put("table", dictConfig.getTable());
        map.put("type", dictConfig.getType());
        map.put("value", dictConfig.getValue());
        map.put("inValue", inValue);
        map.put("typeValue", typeValue);
        map.put("lastSql", lastSql);
        map.put("remark", remark);

        return getString(map, dictMsId, dictConfig.isEnableSqlLog());
    }

    @Override
    public String selectTable(
            String tableName,
            String columnName,
            String valueColumnName,
            String inValue,
            String lastSql,
            String remark) {
        Map<String, String> map = new HashMap<>();
        map.put("tableName", tableName);
        map.put("columnName", columnName);
        map.put("valueColumnName", valueColumnName);
        map.put("inValue", inValue);
        map.put("lastSql", lastSql);
        map.put("remark", remark);
        return getString(map, tableMsId, tableConfig.isEnableSqlLog());
    }

    private String getString(Map<String, String> map, String tableMsId, boolean enableLog) {
        if (enableLog) {
            log.info("translate sql param:{}", map);
            log.info("translate sql sql:{}", cacheMap.get(tableMsId).getBoundSql(map).getSql());
        }
        try (SqlSession sqlSession = factory.openSession()) {
            List<Object> list = sqlSession.selectList(tableMsId, map);
            if (CollUtil.isEmpty(list)) {
                return null;
            } else {
                return list.getFirst().toString();
            }
        }
    }
}
